package com.leff.cards.util;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;

import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;

import com.facebook.android.AsyncFacebookRunner;
import com.facebook.android.AsyncFacebookRunner.RequestListener;
import com.facebook.android.DialogError;
import com.facebook.android.Facebook;
import com.facebook.android.Facebook.DialogListener;
import com.facebook.android.FacebookError;
import com.leff.cards.data.Contacts;
import com.leff.cards.data.Group;
import com.leff.cards.data.QueryCard;
import com.leff.cards.data.ResponseCard;
import com.leff.cards.data.User;
import com.leff.cards.ui.LoginActivity;
import com.leff.notifications.NotificationCenter;

public class FacebookSession implements Secrets {

	public static final String FB_LOGIN_COMPLETE = "FbLoginComplete";
	public static final String FB_LOGIN_FAILED = "FbLoginFailed";
	
	public static final String FB_POST_SUCCESSFUL = "FbPostSuccessful";
	public static final String FB_POST_FAILED = "FbPostFailed";
	
	private Facebook mFacebook;
	private AsyncFacebookRunner mQueryDispatcher;
	
	private Handler mHandler;
	
	private FacebookSession() {
		mFacebook = new Facebook(APP_ID);
		mQueryDispatcher = new AsyncFacebookRunner(mFacebook);
		
		mHandler = new Handler();
	}
	
	public boolean restoreSession(Context C) {
		
		String token = Settings.getFBAccessToken();
		long expiration = Settings.getFBExpiration();
		
		if(token == null || expiration <= 0) {
			return false;
		}
		
		mFacebook.setAccessToken(token);
		mFacebook.setAccessExpires(expiration);
		
		if(mFacebook.isSessionValid()) {
			mFacebook.extendAccessTokenIfNeeded(C, null);
			return true;
		}
		return false;
	}
	
	public void authorize(final LoginActivity host) {
		
		String[] perms = {
			"publish_stream"
		};
		
		Log.v(TAG, "FB.authorize()");
		mFacebook.authorize(host, perms, new DialogListener() {

			@Override
			public void onComplete(Bundle values) {
				String token = mFacebook.getAccessToken();
				long expires = mFacebook.getAccessExpires();

				Log.v(TAG, "Facebook Auth Completed: " + token + " (" + expires + ")");
				
				Settings.setFBAccessToken(token);
				Settings.setFBExpiration(expires);
				
				host.loginComplete();
			}

			@Override
			public void onFacebookError(FacebookError e) {
				Log.v(TAG, "Facebook Auth Error: ", e);
			}

			@Override
			public void onError(DialogError e) {
				Log.v(TAG, "Facebook General Auth Error: ", e);
			}

			@Override
			public void onCancel() {
				Log.v(TAG, "Facebook Cancel.");
			}
		});
	}
	
	public void login() {
		mQueryDispatcher.request("me", new MeRequestListener());
	}
	
	public void postCardSelection(QueryCard QC) {
		
		StringBuilder responseText = new StringBuilder();
		ResponseCard[] cards = QC.getResponses();
		boolean first = true;
		for(ResponseCard RC : cards) {
			if(!first) {
				responseText.append("\n");
			}
			responseText.append(RC.getText());
			first = false;
		}
		
		String message = "I just voted for my favorite answer to:\n" + QC.getText() + "\n\n" + responseText;
		postFeed(message);
	}
	public void postInvitation(long fbid, Group G) {
		
		String message = "I just invited you to join my A*holes to A*holes group \"" + G.name + "\" - Hit it.";
		String user = "" + fbid;
		
		postFeed(user, message);
	}
	public void postFeed(String message) {
		postFeed("me", message);
	}
	public void postFeed(final String user, final String message) {
		
		new Thread(new Runnable() {
			public void run() {
				
				Bundle params = new Bundle();
				params.putString("message", message);
				params.putString("link", APK_URL);
				params.putString("name", "A*holes to A*holes");
				params.putString("caption", "(Android Application Package)");
				params.putString("description", "Deep down, we're all terrible people.");
				
				boolean success = false;
				try {
					String response = mFacebook.request(user + "/feed", params, "POST");
					if(response == null || response.equals("") || response.equals("false")) {
						success = false;
					} else {
						success = true;
					}
				} catch(Exception e) {
					success = false;
				}
				
				final String notif = success ? FB_POST_SUCCESSFUL : FB_POST_FAILED;
				mHandler.post(new Runnable() {
					public void run() {
						NotificationCenter.postNotification(notif, null);
					}
				});
			}
		}).start();
		
	}
	
	public void logout(final Context C) {
		
			new Thread(new Runnable() {
				public void run() {
					try {
						String result = mFacebook.logout(C);
						Log.v(TAG, "Facebook.logout() response: " + result);
						
						Settings.clearFacebookCredentials();
						
					} catch(Exception e) {
						Log.e(TAG, "Facebook.logout() exception: ", e);
					}
				}
			}).start();
	}
	
	private void requestFriends() {
		mQueryDispatcher.request("me/friends", new FriendsRequestListener());
	}
	
	public void authorizeCallback(int requestCode, int resultCode, Intent data) {
		mFacebook.authorizeCallback(requestCode, resultCode, data);
	}

	private static FacebookSession instance;
	public static FacebookSession getSession() {
		if(instance == null) {
			instance = new FacebookSession();
		}
		return instance;
	}
	
	private class MeRequestListener extends BaseRequestListener {

		@Override
		public void onSuccess(final Dictionary data) {
			
			Object error = data.get("error");
			if(error != null) {
				Dictionary errData = Dictionary.fromJSONObject(error);
				String message = (String)errData.get("message");
				
				Log.e(TAG, "Facebook Error: " + message);
				
				Settings.clearFacebookCredentials();
				
				NotificationCenter.postNotification(FB_LOGIN_FAILED, message);
				return;
			}

			long id = Long.parseLong(data.get("id").toString());
			String name = (String)data.get("name");
			User U = new User(id, name);
			NotificationCenter.postNotification(FB_LOGIN_COMPLETE, U);
			
			FacebookSession.this.requestFriends();
		}
	}
	
	private class FriendsRequestListener extends BaseRequestListener {

		@Override
		public void onSuccess(Dictionary data) {
			Contacts.initFromFacebookData(data);
		}
	}

	private abstract class BaseRequestListener implements RequestListener {

		public abstract void onSuccess(Dictionary data);
		public void onFailure(String message) {}
		
		@Override
		public void onComplete(String response, Object state) {
			Log.v(TAG, "Facebook: Request Complete");
			
			JSONParser parser = new JSONParser();
			
			try {
				JSONObject obj = (JSONObject)parser.parse(response);
				final Dictionary data = Dictionary.fromJSONObject(obj);
				
				mHandler.post(new Runnable() {
					public void run() {
						onSuccess(data);
					}
				});
				
			} catch(Exception e) {
				Log.v(TAG, "Error Parsing Facebook Response:", e);
			}
		}

		@Override
		public void onIOException(IOException e, Object state) {
			Log.v(TAG, "Facebook: File Not Found", e);
			postFailure("Connection Failed.");
		}

		@Override
		public void onFileNotFoundException(FileNotFoundException e, Object state) {
			Log.v(TAG, "Facebook: File Not Found", e);
			postFailure("Connection Failed!");
		}

		@Override
		public void onMalformedURLException(MalformedURLException e, Object state) {
			Log.v(TAG, "Facebook: Malformed URL", e);
			postFailure(e.getMessage());
		}

		@Override
		public void onFacebookError(FacebookError e, Object state) {
			Log.v(TAG, "Facebook: Error", e);
			postFailure(e.getMessage());
		}
		
		private void postFailure(final String message) {
			mHandler.post(new Runnable() {
				public void run() {
					onFailure(message);
				}
			});
		}
	}
}
